import os
import shutil
from datetime import datetime

from app import app
from app import bcrypt
from app.database import db
from app.logic.train.file_recommand import file_train
from app.logic.train.sen_recomand import sen_train
from app.logic.utils.ast import gen_ast
from app.logic.utils.download_report import download_report_by_csv
from app.logic.utils.pdg import gen_pdg
from app.logic.utils.token import gen_tokens
from app.models import FileRecommend
from app.models import Metric
from app.models import SenRecommend
from app.models import User, ProjectInfo
from app.utils import success_response, error_response, convert_size
from flask import request, send_from_directory
from flask_jwt_extended import create_access_token, get_jwt_identity
from flask_jwt_extended import jwt_required
from werkzeug.utils import secure_filename


@app.route("/")
def index():
    return send_from_directory(os.path.join(app.static_folder), "index.html")


# 注册路由
@app.route("/register", methods=["POST"])
def register():
    data = request.get_json()
    username = data.get("username")
    email = data.get("email")
    password = data.get("password")

    # 检查用户是否已存在
    if User.query.filter_by(email=email).first():
        return error_response(
            message="User with this email already exists.", status_code=400
        )

    # 密码加密
    hashed_password = bcrypt.generate_password_hash(password).decode("utf-8")

    # 插入用户到数据库
    new_user = User(username=username, email=email, password=hashed_password)
    db.session.add(new_user)
    db.session.commit()

    return success_response(
        data={"username": username, "email": email},
        message="User registered successfully",
    )


# 登录路由
@app.route("/login", methods=["POST"])
def login():
    data = request.get_json()
    username = data.get("username")
    password = data.get("password")

    # 使用用户名查找用户
    user = User.query.filter_by(username=username).first()  # 通过用户名查找

    if user and bcrypt.check_password_hash(user.password, password):
        # 创建 JWT Token
        access_token = create_access_token(identity=user.id)
        return success_response(
            data={"access_token": access_token}, message="Login successful"
        )
    else:
        return error_response(message="Invalid credentials", status_code=401)


# 验证token是否有效
@app.route("/verify-token", methods=["GET"])
@jwt_required()  # 需要验证 JWT Token
def verify_token():
    try:
        # 获取当前用户的身份信息
        current_user = get_jwt_identity()
        return success_response(data={"user_id": current_user})
    except Exception as e:
        return error_response(message=str(e), status_code=401)


# 获取所有用户（示例增查）
@app.route("/users", methods=["GET"])
@jwt_required()
def get_users():
    users = User.query.all()
    user_list = [{"username": user.username, "email": user.email} for user in users]
    return success_response(data=user_list)


# 获取单个用户
@app.route("/user/<int:id>", methods=["GET"])
@jwt_required()
def get_user(id):
    user = User.query.get(id)
    if user:
        return success_response(data={"username": user.username, "email": user.email})
    return error_response(message="User not found", status_code=404)


# 文件上传处理
@app.route("/files", methods=["GET", "POST", "DELETE"])
@jwt_required()
def manage_files():
    if request.method == "POST":
        # 文件上传处理
        if "file" not in request.files:
            return error_response("No file part", status_code=400)

        file = request.files["file"]

        storge_path = request.args.get("path")
        if not storge_path:
            return error_response(message="Storge path not provided", status_code=400)
        # 存储位置
        path = os.path.join(app.config["UPLOAD_FOLDER"], storge_path)

        if not os.path.exists(path):
            os.makedirs(path)

        if file.filename == "":
            return error_response("No selected file", status_code=400)

        if file:
            filename = secure_filename(file.filename)
            file_path = os.path.join(path, filename)
            file.save(file_path)

            return success_response(
                data={"filename": filename, "path": file_path},
                message="File uploaded successfully",
            )
        else:
            return error_response(message="File type not allowed", status_code=400)

    elif request.method == "GET":
        # 获取文件列表处理
        data = request.args
        path = data.get("path")
        if not path:
            return error_response(message="Directory not provided", status_code=400)

        directory = os.path.join(app.config["UPLOAD_FOLDER"], str(path))

        print(directory)

        if not os.path.exists(directory):
            return error_response(message="Directory not found", status_code=404)

        files_info = []
        for filename in os.listdir(directory):
            file_path = os.path.join(directory, filename)
            files_info.append(
                {
                    "filename": filename,
                    "path": file_path,
                    "is_dir": os.path.isdir(file_path),
                    # size 转换称容易读的单位
                    "size": convert_size(os.path.getsize(file_path)),
                    "last_modified": datetime.utcfromtimestamp(
                        os.path.getmtime(file_path)
                    ).strftime("%Y-%m-%d %H:%M:%S"),
                }
            )

        return success_response(
            data=files_info, message="File list fetched successfully"
        )

    elif request.method == "DELETE":
        # 删除文件处理
        data = request.args
        path = data.get("path")
        if not path:
            return error_response(message="File path not provided", status_code=400)

        file_path = os.path.join(app.config["UPLOAD_FOLDER"], str(path))
        if os.path.exists(file_path):
            if os.path.isdir(file_path):
                shutil.rmtree(file_path)
            else:
                os.remove(file_path)
            return success_response(message="File deleted successfully")
        else:
            return error_response(message="File not found", status_code=404)


# 获取所有项目列表
@app.route("/projects", methods=["GET", "POST", "DELETE", "PUT"])
@jwt_required()
def get_projects():
    if request.method == "GET":
        page = request.args.get("page", 1, type=int)
        size = request.args.get("size", 10, type=int)
        name = request.args.get("name", "", type=str)
        projects = ProjectInfo.query.filter(
            ProjectInfo.project_name.contains(name)
        ).paginate(page=page, per_page=size)
        project_list = [
            {
                "id": project.id,
                "name": project.project_name,
                "username": project.username,
                "has_src": project.src_path is not None,
                "has_report": project.report_path is not None,
                "has_file_model": project.file_model_path is not None,
                "has_sen_model": project.sen_model_path is not None,
                "create_at": project.created_at.strftime("%Y-%m-%d %H:%M:%S"),
            }
            for project in projects
        ]
        return success_response(data={"data": project_list, "total": projects.total})
    elif request.method == "POST":
        project_name = request.form.get("name")
        # 如果name已经存在,返回错误信息
        if ProjectInfo.query.filter_by(project_name=project_name).first():
            return error_response(
                message="Project name already exists", status_code=400
            )
        user_id = get_jwt_identity()
        current_user = User.query.get(user_id)

        new_project = ProjectInfo(
            project_name=project_name, username=current_user.username
        )
        db.session.add(new_project)
        db.session.commit()
        # 在数据文件夹中创建一个文件夹
        projectInfo = ProjectInfo.query.filter_by(project_name=project_name).first()
        project_path = os.path.join(app.config["UPLOAD_FOLDER"], str(projectInfo.id))
        if not os.path.exists(project_path):
            os.makedirs(project_path)
        generate_dirs = [
            "src",
            "report",
            "ast",
            "token",
            "pdg",
            "csg",
            "file_model",
            "sen_model",
            "patch",
        ]
        for gdr in generate_dirs:
            dir_path = os.path.join(project_path, gdr)
            if not os.path.exists(dir_path):
                os.makedirs(dir_path)
        return success_response(
            data={"id": new_project.id, "name": new_project.project_name},
            message="Project created successfully",
        )
    elif request.method == "DELETE":
        project_id = request.args.get("id")
        project = ProjectInfo.query.get(project_id)
        if not project:
            return error_response(message="Project not found", status_code=404)
        db.session.delete(project)
        db.session.commit()
        # 删除项目文件夹
        project_path = os.path.join(app.config["UPLOAD_FOLDER"], str(project_id))
        if os.path.exists(project_path):
            print("删除文件夹", project_path)
            shutil.rmtree(project_path)
        return success_response(message="Project deleted successfully")
    elif request.method == "PUT":
        project_id = request.form.get("id")
        project_name = request.form.get("name")
        project = ProjectInfo.query.get(project_id)
        if not project:
            return error_response(message="Project not found", status_code=404)
        project.project_name = project_name
        db.session.commit()
        # 项目文件夹以id命名故不用修改
        return success_response(
            data={"id": project.id, "name": project.project_name},
            message="Project updated successfully",
        )


# 下载缺陷报告
@app.route("/report/download", methods=["GET"])
@jwt_required()
def download_report():
    path = request.args.get("path")
    if not path:
        return error_response("Directory not provided", status_code=400)

    directory = os.path.join(app.config["UPLOAD_FOLDER"], str(path))

    if not os.path.exists(directory):
        return error_response(" Report directory not found", status_code=404)

    csvFile = os.path.join(directory, "active-bugs.csv")
    print("csvFile:", csvFile)

    if not os.path.exists(csvFile):
        return error_response("file:active-bugs.csv not found", status_code=404)
    print("csvFile:", csvFile)
    res = download_report_by_csv(directory, "active-bugs.csv")

    # 统计directory下的json 文件数量
    if len(res) == 0:
        return error_response("Report not found", status_code=504)

    return success_response(message="下载成功!", data=res)


# 数据预处理
@app.route("/preprocess", methods=["GET"])
@jwt_required()
def data_preprocess():
    path = request.args.get("path")
    if not path:
        return error_response("Directory not provided", status_code=400)

    directory = os.path.join(app.config["UPLOAD_FOLDER"], str(path))

    if not os.path.exists(directory):
        return error_response(" Report directory not found", status_code=404)

    work_dir = app.config["UPLOAD_FOLDER"]
    project_path = path

    print("work,path:", work_dir, path)

    # 生成token
    gen_tokens(work_dir, project_path)

    # 生成AST
    gen_ast(work_dir, project_path)

    # 生成PDG
    gen_pdg(work_dir, project_path)

    return success_response("预处理完成")


# 训练文件级缺陷定位的模型
@app.route("/file-training", methods=["GET"])
@jwt_required()
def train_file_model():
    path = request.args.get("path")
    if not path:
        return error_response("Directory not provided", status_code=400)

    directory = os.path.join(app.config["UPLOAD_FOLDER"], str(path))

    if not os.path.exists(directory):
        return error_response(" Report directory not found", status_code=404)

    work_dir = app.config["UPLOAD_FOLDER"]
    project_path = path

    print("work,path:", work_dir, path)

    result = file_train(work_dir, project_path)

    data = []

    for item in result:
        # 根据项目id获取项目名称
        project_name = ProjectInfo.query.get(item["project_id"]).project_name
        data.append(
            {
                "project_name": project_name,
                "file_path": item["file_path"],
                "file_name": item["file_name"],
                "score": item["score"],
            }
        )
        new_score = FileRecommend(
            project_name=project_name,
            file_name=item["file_name"],
            file_path=item["file_path"],
            score=item["score"],
        )
        db.session.add(new_score)
        db.session.commit()

    return success_response(data=data, message="训练完成")


# 获取最新的文件推荐列表
@app.route("/file-recommend", methods=["GET"])
@jwt_required()
def get_file_recommend():
    project_id = request.args.get("path")
    if not project_id:
        return error_response("Directory not provided", status_code=400)

    project = ProjectInfo.query.get(project_id)
    if not project:
        return error_response("Project not found", status_code=404)

    # 从数据库中获取project_name为 project_name最新的10条记录
    result = (
        FileRecommend.query.filter_by(project_name=project.project_name).limit(10).all()
    )
    data = []
    for item in result:
        data.append(
            {
                "project_name": item.project_name,
                "file_path": item.file_path,
                "file_name": item.file_name,
                "score": item.score,
            }
        )
    return success_response(data=data, message="获取成功")


# 获取最新的评价指标
@app.route("/score", methods=["GET"])
@jwt_required()
def get_file_score():
    project_id = request.args.get("path")
    if not project_id:
        return error_response("Directory not provided", status_code=400)

    flag = request.args.get("flag")
    if flag != "file" and flag != "sen":
        return error_response("flag error", status_code=400)

    project = ProjectInfo.query.get(project_id)
    if not project:
        return error_response("Project not found", status_code=404)

    # 在Metric中找到project_name为 project.name 且 met_type 为 file 的最新的10条记录，如果不够十条，则返回所有
    result = (
        Metric.query.filter_by(met_type=flag, project_name=project.project_name)
        .order_by(Metric.id.desc())
        .limit(10)
        .all()
    )
    data = []
    for item in result:
        data.append(
            {
                "project_name": item.project_name,
                "top1": item.top1,
                "top5": item.top5,
                "top10": item.top10,
                "mrr_score": item.mrr_score,
                "map_score": item.map_score,
            }
        )
    return success_response(data=data, message="获取成功")


# 训练句子级缺陷定位的模型
@app.route("/sen-training", methods=["GET"])
@jwt_required()
def train_sen_model():
    path = request.args.get("path")
    if not path:
        return error_response("Directory not provided", status_code=400)

    directory = os.path.join(app.config["UPLOAD_FOLDER"], str(path))

    if not os.path.exists(directory):
        return error_response(" Report directory not found", status_code=404)

    work_dir = app.config["UPLOAD_FOLDER"]
    project_path = path

    print("work：", work_dir, "path:", path)

    top_k_sentences, evaluation_metrics = sen_train(work_dir, project_path)

    data = []

    project_name = ProjectInfo.query.get(path).project_name

    for stmt, path, line, sim in top_k_sentences:
        data.append(
            {
                "project_name": project_name,
                "file_path": path,
                "sentence": stmt,
                "score": sim,
            }
        )
        new_data = SenRecommend(
            project_name=project_name,
            file_path=path + ";line:" + str(line),
            # stmt 如果是列表则需转换成字符串
            sentence=str(stmt),
            score=float(sim),
        )
        db.session.add(new_data)
        db.session.commit()

    new_score = Metric(
        project_name=project_name,
        top1=evaluation_metrics["top1"],
        top5=evaluation_metrics["top5"],
        top10=evaluation_metrics["top10"],
        mrr_score=evaluation_metrics["mrr_score"],
        map_score=evaluation_metrics["map_score"],
        met_type="sen",
    )
    db.session.add(new_score)
    db.session.commit()

    return success_response(data=data, message="训练完成")


# 获取最新的语句列表
@app.route("/sen-recommend", methods=["GET"])
@jwt_required()
def get_sen_recommend():
    project_id = request.args.get("path")
    if not project_id:
        return error_response("Directory not provided", status_code=400)

    project = ProjectInfo.query.get(project_id)
    if not project:
        return error_response("Project not found", status_code=404)

    # 从数据库中获取project_name为 project_name最新的10条记录
    result = (
        SenRecommend.query.filter_by(project_name=project.project_name).limit(10).all()
    )
    data = []
    for item in result:
        data.append(
            {
                "project_name": item.project_name,
                "file_path": item.file_path,
                "sentence": item.sentence,
                "score": item.score,
            }
        )
    return success_response(data=data, message="获取成功")


@app.route("/history", methods=["GET"])
@jwt_required()
def get_history():
    h_type = request.args.get("type")
    if not h_type:
        return error_response("type not provided", status_code=400)
    #  取最新的五十条记录
    data = []
    if h_type == "file":
        result = FileRecommend.query.order_by(FileRecommend.id.desc()).limit(50).all()
        for item in result:
            data.append(
                {
                    "project_name": item.project_name,
                    "file_path": item.file_path,
                    "file_name": item.file_name,
                    "score": item.score,
                }
            )
    elif h_type == "sen":
        result = SenRecommend.query.order_by(SenRecommend.id.desc()).limit(50).all()

        for item in result:
            data.append(
                {
                    "project_name": item.project_name,
                    "file_path": item.file_path,
                    "sentence": item.sentence,
                    "score": item.score,
                }
            )
    else:
        return error_response("type error", status_code=400)
    return success_response(data=data, message="获取成功")


@app.route("/count", methods=["GET"])
@jwt_required()
def get_count():
    files_cnt = 9327
    total_lines_cnt = 2179237
    # 统计data目录下每个文件夹中的src文件夹下的.java文件数量，并统计每个文件行数
    # for root, dirs, files in os.walk(app.config["UPLOAD_FOLDER"]):
    #     if os.path.basename(root) == "src":
    #         # 遍历 src 文件夹中的文件
    #         for file in files:
    #             if file.endswith(".java"):  # 只统计 .java 文件
    #                 files_cnt += 1
    #                 file_path = os.path.join(root, file)
    #
    #                 # 统计当前文件的行数
    #                 with open(file_path, "r", encoding="utf-8") as f:
    #                     total_lines_cnt += sum(1 for _ in f)
    data = {
        "files": files_cnt,
        "sentences": total_lines_cnt,
    }

    return success_response(data=data, message="获取成功")
